package se.topshelf.golftracker.core.domain;

import org.hibernate.annotations.Sort;
import org.hibernate.annotations.SortType;

import javax.persistence.*;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import java.text.MessageFormat;
import java.util.SortedSet;
import java.util.TreeSet;

/**
 *  Describes a golf shot and its characteristics (club used, length and other attributes).
 */
@Entity
@Table(name = "Shot")
public class Shot extends BaseEntity implements Comparable<Shot> {
    @NotNull
    @ManyToOne
    @JoinColumn(name = "holeResultId")
    private HoleResult holeResult;

    @NotNull
    @ManyToOne
    @JoinColumn(name = "clubId")
    private Club club;

    @Min(0)
    private Integer lengthInMeters;

    @NotNull
    @Min(0)  // Zero based index. 0 is always the first shot
    private Integer shotOrder;

    @NotNull
    @Enumerated(EnumType.STRING)
    private ShotDirection direction;

    @NotNull
    @Enumerated(EnumType.STRING)
    private ShotLength length;

    @ElementCollection
    @CollectionTable
    @Sort(type = SortType.NATURAL)
    @Enumerated(EnumType.STRING)
    private SortedSet<ShotCharacteristic> characteristics = new TreeSet<ShotCharacteristic>();

    private String description;


    public Shot() {
    }

    public Shot(Club club, Integer lengthInMeters, ShotDirection direction, ShotLength length) {
        this.club = club;
        this.lengthInMeters = lengthInMeters;
        this.direction = direction;
        this.length = length;
    }

    public HoleResult getHoleResult() {
        return holeResult;
    }

    // Use HoleResult.addShot()
    public void setHoleResult(HoleResult holeResult) {
        this.holeResult = holeResult;
    }

    public Club getClub() {
        return club;
    }

    public void setClub(Club club) {
        this.club = club;
    }

    public Integer getLengthInMeters() {
        return lengthInMeters;
    }

    public void setLengthInMeters(Integer lengthInMeters) {
        this.lengthInMeters = lengthInMeters;
    }

    public Integer getShotOrder() {
        return shotOrder;
    }

    public void setShotOrder(Integer shotOrder) {
        this.shotOrder = shotOrder;
    }

    public ShotDirection getDirection() {
        return direction;
    }

    public void setDirection(ShotDirection direction) {
        this.direction = direction;
    }

    public ShotLength getLength() {
        return length;
    }

    public void setLength(ShotLength length) {
        this.length = length;
    }

    public SortedSet<ShotCharacteristic> getCharacteristics() {
        return characteristics;
    }

    public void addShotCharacteristic(ShotCharacteristic characteristic){
        if(characteristics.contains(characteristic)){
            throw new IllegalArgumentException("");
        }
        characteristics.add(characteristic);
    }

    public void removeShotCharacteristic(ShotCharacteristic characteristic){
        if(characteristics.contains(characteristic)){
            throw new IllegalArgumentException(MessageFormat.format(COULD_NOT_REMOVE_0_DID_NOT_EXIST_ON_1, characteristic, this));
        }
    }

    public void setCharacteristics(SortedSet<ShotCharacteristic> characteristics) {
        this.characteristics = characteristics;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public int compareTo(Shot o) {
        return shotOrder.compareTo(o.getShotOrder());
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Shot shot = (Shot) o;

        if (!shotOrder.equals(shot.shotOrder)) return false;

        return true;
    }

    @Override
    public int hashCode() {
        return shotOrder.hashCode();
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder();
        sb.append("Shot");
        sb.append("{club=").append(club);
        sb.append(", lengthInMeters=").append(lengthInMeters);
        sb.append(", shotOrder=").append(shotOrder);
        sb.append(", direction=").append(direction);
        sb.append(", length=").append(length);
        sb.append('}');
        return sb.toString();
    }
}
